package model.path;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Stack;

import matrix.GraphDoor;
import matrix.GraphNode;
import matrix.NodeLinkMatrix;
import model.DoorModel;
import model.RoomModel;

public class PathFactory 
{
	private PathConstraint currentConstraint;
	private NodeLinkMatrix matrix;	
	
	public PathFactory(NodeLinkMatrix matrix)
	{
		this.matrix = matrix;
	}

	public Path generatePath()
	{
		Path path = new Path();
				
		if(currentConstraint.stopPoints.size() == 0)
		{
			path.addAll( this.generateSubpath(this.currentConstraint.getBeginPoint(), this.currentConstraint.getEndPoint() ));
			return path;
		}
		path.addAll(this.generateSubpath(this.currentConstraint.getBeginPoint(), this.currentConstraint.stopPoints.get(0)));
		
		for(int i = 0; i < currentConstraint.stopPoints.size() - 1; i++)
		{
			path.addAll(this.generateSubpath(currentConstraint.stopPoints.get(i), currentConstraint.stopPoints.get(i+1)));
		}
		path.addAll(this.generateSubpath(currentConstraint.stopPoints.get(currentConstraint.stopPoints.size()-1), this.currentConstraint.getEndPoint()));
		return path;
	}

	private ArrayList<PathStopPoint> generateSubpath(PathStopPoint beginStopPoint,	PathStopPoint endStopPoint) 
	{
		DijkstraResult shortestPath;
		double totalLength = 0;
		
		GraphNode beginGraphNode = beginStopPoint.getNode();
		GraphNode endGraphNode = endStopPoint.getNode();
				
		RoomModel beginRoom = beginGraphNode.getRoom();
		RoomModel endRoom = endGraphNode.getRoom();
		
		ArrayList<GraphNode> resultBeforeTiming = new ArrayList<GraphNode>();
		Stack<GraphNode> rooms = new Stack<GraphNode>();
		

		if(beginRoom != endRoom)
		{
			GraphNode beginNode = null;
			GraphNode endNode = null;
			for(Iterator<GraphNode> it = this.matrix.getKeys().iterator(); it.hasNext();)
			{
				GraphNode node = it.next();
				if(node.getRoom() == beginRoom)
				{
					beginNode = node;
				}
				else if(node.getRoom() == endRoom)
				{
					endNode = node;
				}
			}	
				
			rooms = this.matrix.getShortestPath(beginNode, endNode).getNodes();
		}
		else
		{
			rooms.add(beginGraphNode);
		}
		
		GraphNode entryNode = null;
		GraphNode endNode = null;
		
		RoomModel currentRoom = null;
		RoomModel nextRoom = null;
		
		DoorModel lastDoor = null;
		
		ArrayList<GraphDoor> doorNodes = new ArrayList<GraphDoor>();
		
		for(int i = 0; i < rooms.size(); i++)
		{
			doorNodes.clear();
			currentRoom = (RoomModel) rooms.get(i).getRoom();
			if(i < rooms.size() - 1)
			{
				nextRoom = (RoomModel) rooms.get(i+1).getRoom();
				for(Iterator<GraphNode> it = currentRoom.getMatrix().getKeys().iterator(); it.hasNext();)
				{
					GraphNode currentNode = it.next();
					if(currentNode.getClass() == GraphDoor.class)
					{
						DoorModel currentDoor = ((GraphDoor)currentNode).getDoorModel();
						if( (currentDoor.FirstRoom == currentRoom && currentDoor.SecondRoom == nextRoom)
								|| (currentDoor.FirstRoom == nextRoom && currentDoor.SecondRoom == currentRoom))
						{
							doorNodes.add((GraphDoor) currentNode);
						}
					}
				}
			}

			

			Collections.shuffle(doorNodes);

			if(rooms.size() == 1)
			{
				entryNode = beginGraphNode;
				endNode = endGraphNode;
			}
			else if( i == rooms.size()-1)
			{
				entryNode = currentRoom.getInnerGraphDoor(lastDoor);
				endNode = endStopPoint.getNode();
			}
			else if(i==0)
			{
				entryNode = beginStopPoint.getNode();
				endNode = doorNodes.get(0);
			}
			else
			{
				entryNode = currentRoom.getInnerGraphDoor(lastDoor);
				endNode = doorNodes.get(0);
			}
			
			if(rooms.size() > 1 && doorNodes.size() > 0)
			{
				lastDoor = doorNodes.get(0).getDoorModel();
			}

			shortestPath = currentRoom.getMatrix().getShortestPath(entryNode, endNode);
			resultBeforeTiming.addAll(shortestPath.getNodes());
			totalLength += shortestPath.getLength();
		}
		long timeIntervalle =  0;
		long timeStep = (long) (timeIntervalle / totalLength);
		if(totalLength == 0)
		{
			timeStep = 0;
		}
		
		ArrayList<PathStopPoint> result = new ArrayList<PathStopPoint>();
		for(int i = 0; i < resultBeforeTiming.size(); i++)
		{
			GraphNode currentNode = resultBeforeTiming.get(i);
			PathStopPoint currentStopPoint = new PathStopPoint(currentNode);
			if(i == resultBeforeTiming.size()-1)
			{
				currentStopPoint.setStayTime(endStopPoint.getStayTime());
			}
		
			result.add(currentStopPoint);
		}
		return result;
	}
	
	public void setContstraints(PathConstraint pathConstraint)
	{
		this.currentConstraint = pathConstraint;
	}
}
